﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.Diagnostics;
using ElfTyping.General;

namespace ElfTyping.Animations
{
    public class Animation
    {
        string filename = ""; // to identify animations for debug purposes

        public bool FlippedHorizontally { get; set; }
        
        bool animateOnce = false;
        bool animateBackwards = false;
        bool finishedAnimatingOnce = false;

        protected Texture2D texture = null;

        protected List<Rectangle> Frames { get; set; }
        int frameCount;
        public float FrameTime { get; set; }

        int currentFrame;	// 0...n-1
        float currentTime;

        static Animation _blankAnimation = null;
        public static Animation EmptyAnimation
        {
            get
            {
                if (_blankAnimation == null)
                {
                    _blankAnimation = new BlankAnimation();
                }

                return _blankAnimation;
            }
        }

        public Animation(string imageFile) : this(imageFile, 1, 0.5f)
        {
            Frames = new List<Rectangle>() {
                new Rectangle(0,0, texture.Width, texture.Height)
            };
        }

        public Animation(string imageFile, Rectangle sourceRect)
            : this(imageFile, 1, 0.5f)
        {
            Frames = new List<Rectangle>() {
                sourceRect
            };
        }

        public Animation(string imageFile, int numOfFrames, float timeBetween)
            : this(imageFile, numOfFrames, timeBetween, false, false) { }

        public Animation(string imageFile, int numOfFrames, float timeBetween, bool once, bool backwards)
        {
	        Debug.Assert( numOfFrames > 0 && numOfFrames < 64, "numOfFrames not in proper range."  );

            filename = imageFile;
            FrameTime = timeBetween;
            frameCount = numOfFrames;
            FlippedHorizontally = false;

            texture = TypingGame.Game().Content.Load<Texture2D>(imageFile);

            /* There is now no default for Frames and must be set manually. */

            if (once)
            {
                SetAnimateOnce();
            }

            if (backwards)
            {
                SetAnimateBackwards();
            }

        }

        public virtual void Advance(float dt)
        {
	        if ( frameCount > 1 && !finishedAnimatingOnce ) {
		        currentTime += dt;
                if (currentTime >= FrameTime)
                {
                    int framesAdvanced = ((int)(currentTime / FrameTime));
                    // e.g., if current time was 1.3 and the frame time was .5, we would have advanced 2 frames.

                    if (animateOnce)
                    {
                        if (animateBackwards)
                        {
                            currentFrame -= framesAdvanced;
                            if (currentFrame <= 0)
                            {
                                currentFrame = 0;
                                finishedAnimatingOnce = true;
                            }
                        }
                        else
                        {
                            currentFrame += framesAdvanced;
                            if (currentFrame >= frameCount - 1)
                            {
                                currentFrame = frameCount - 1;
                                finishedAnimatingOnce = true;
                            }
                        }
                    }
                    else
                    {
                        if (animateBackwards)
                        {
                            currentFrame = (currentFrame - framesAdvanced + frameCount) % frameCount;
                        }
                        else
                        {
                            currentFrame = (currentFrame + framesAdvanced) % frameCount;
                        }
                        // from the former example, if the current frame was 4 (out of 5 frames), the new frame would
                        //   be 1 (4+2 mod 5) and the current time would be reset to 0.3 (1.3 - .5*2).
                    }
                    currentTime -= FrameTime * framesAdvanced;
                }

                
	        }
        }

        public void BlitCenteredAt(Camera camera, Point center)
        {
            Rectangle destination = new Rectangle((int)center.X - Frames[currentFrame].Width / 2, (int)center.Y - Frames[currentFrame].Height / 2, Frames[currentFrame].Width, Frames[currentFrame].Height);
            Draw(camera, destination);
        }

        public void Draw(Camera camera, Rectangle destination)
        {
            Draw(camera, destination, Color.White);
        }

        public void Draw(Camera camera, Rectangle destination, float depth)
        {
            Draw(camera, destination, Color.White, depth);
        }

        public void Draw(Camera camera, Rectangle destination, Color color)
        {
            Draw(camera, destination, color, 0.5f);
        }

        public virtual void Draw(Camera camera, Rectangle destination, Color color, float depth)
        {
            camera.DrawRectangleUV(destination, texture, Frames[currentFrame], color, FlippedHorizontally);
        }

        public void Reset()
        {
            currentFrame = (animateBackwards) ? (frameCount - 1) : (0);
	        currentTime = 0;
            finishedAnimatingOnce = false;
        }

        public void SetAnimateOnce()
        {
            animateOnce = true;

            if (animateBackwards)
            {
                currentFrame = frameCount - 1;
            }
            else
            {
                currentFrame = 0;
            }

            finishedAnimatingOnce = false;
        }

        public void SetAnimateBackwards()
        {
            animateBackwards = true;
            currentFrame = frameCount - 1;
        }

        public void SetToEnd()
        {
            if (animateBackwards)
            {
                currentFrame = 0;
            }
            else
            {
                currentFrame = frameCount - 1;
            }

            finishedAnimatingOnce = animateOnce;
        }

        private class BlankAnimation : Animation
        {
            public BlankAnimation() : base(@"Textures\White") { }

            public override void Draw(Camera camera, Rectangle destination, Color color, float depth)
            {
                // Do nothing.
            }
        }
    }    
}
